package whf.framework.lucene.index;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.RAMDirectory;

import whf.framework.entity.Entity;
import whf.framework.entity.PropertyTypeEnum;
import whf.framework.ext.DictManagerImp;
import whf.framework.ext.entity.DictItem;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.log.env.Environment;
import whf.framework.lucene.IndexConfig;
import whf.framework.meta.MetaManager;
import whf.framework.meta.entity.ObjectProperty;
import whf.framework.meta.service.ObjectService;
import whf.framework.meta.service.ObjectServiceImp;
import whf.framework.util.BeanUtils;
import whf.framework.util.StringUtils;
import whf.framework.util.Utils;
import whf.framework.web.struts.AbstractStrutsForm;

/**
 * @author king
 * @create 2008-1-24 上午09:56:18
 * 
 */
public class EntityIndexManager {
	protected Log log = LogFactory.getLog(this.getClass());

	protected boolean useRAMDirectory = true;

	protected Analyzer analyzer;

	public EntityIndexManager(Analyzer analyzer, boolean useRAMDirectory) {
		this.analyzer = analyzer;
		this.useRAMDirectory = useRAMDirectory;
	}

	protected void indexEntitiesDirectly(List<? extends Entity> entities) throws IOException {
		IndexWriter fsWriter = new IndexWriter(IndexConfig.getIndexDirectory(), analyzer, true);
		fsWriter.mergeFactor = IndexWriter.DEFAULT_MERGE_FACTOR;
		fsWriter.maxMergeDocs = IndexWriter.DEFAULT_MAX_MERGE_DOCS;
		//
		addDocuments(fsWriter, entities);
		//
		fsWriter.close();
	}

	public void indexEntities(List<? extends Entity> entities) throws IOException {
		RAMDirectory ramDir = new RAMDirectory();
		IndexWriter ramWriter = new IndexWriter(ramDir, analyzer, true);
		// add docs
		addDocuments(ramWriter, entities);
		//
		IndexWriter fsWriter = new IndexWriter(IndexConfig.getIndexDirectory(), analyzer, true);
		fsWriter.addIndexes(new Directory[] { ramDir });
		ramWriter.optimize();
		ramWriter.close();
		fsWriter.optimize();
		fsWriter.close();
	}

	protected void addDocuments(IndexWriter writer, List<? extends Entity> entities) {
		Set<Long> objectIds = Utils.newHashSet();
		for(Entity entity: entities) {
			try {
				whf.framework.meta.entity.Object meta = MetaManager.getDefaultInstance().findByEntityClass(entity.getClass().getName());
				objectIds.add(meta.getId());
				Set<ObjectProperty> properties = meta.getProperties();
				// 增加元数据
				Document doc = new Document();
				doc.add(Field.Text("class", entity.getClass().getName()));
				doc.add(Field.Text("id", String.valueOf(entity.getId())));
				doc.add(Field.Text("uid", getEntityUid(entity)));
				//
				for (ObjectProperty prop : properties) {
					String indexType = prop.getIndexType();
					if (StringUtils.isEmpty(indexType) || StringUtils.equalsIgnoreCase(indexType, "ignore"))
						continue;
	
					String propertyName = prop.getName();
					if(!BeanUtils.hasProperty(entity.getClass(), propertyName)) continue;
					if(BeanUtils.hasProperty(AbstractStrutsForm.class, propertyName)) continue;
					Object propValue = BeanUtils.getProperty(entity, propertyName);
					if (propValue == null) continue;
					//
					PropertyTypeEnum propertyType = BeanUtils.getPropertyType(propValue, prop);
					
					Field field = null;
					if (StringUtils.equalsIgnoreCase(indexType, "TEXT")) {
						field = Field.Text(propertyName, propValue.toString());
					} else if (StringUtils.equalsIgnoreCase(indexType, "KEYWORD")) {
						field = Field.Keyword(propertyName, propValue.toString());
					} else if (StringUtils.equalsIgnoreCase(indexType, "UNINDEXED")) {
						field = Field.UnIndexed(propertyName, propValue.toString());
					} else if (StringUtils.equalsIgnoreCase(indexType, "UNSTORED")) {
						field = Field.UnStored(propertyName, propValue.toString());
					} else {
						if (propertyType == PropertyTypeEnum.ENTITY) {
							field = Field.Text(propertyName, String.valueOf(((Entity) propValue).getId()));
						} else if (propertyType == PropertyTypeEnum.BOOLEAN) {
							field = Field.Keyword(propertyName, propValue.toString());
						} else if (propertyType == PropertyTypeEnum.COLLECTION) {
							continue;
						} else if (propertyType == PropertyTypeEnum.DATETIME) {
							field = Field.Keyword(propertyName, (Date) propValue);
						} else if (propertyType == PropertyTypeEnum.DICT) {
							DictItem di = DictManagerImp.getDictManager().findDictItem(prop.getDictCategoryCode(), propValue.toString());
							if (di != null) {
								field = Field.Text(propertyName, di.getName());
							}
						} else if (propertyType == PropertyTypeEnum.FILE || propertyType == PropertyTypeEnum.IMAGEFILE) {
							field = Field.UnIndexed(propertyName, propValue.toString());
						} else {
							field = Field.Text(propertyName, propValue.toString());
						}
					}
					if (field != null) {
						doc.add(field);
					}
				}
				writer.addDocument(doc);
			} catch (Exception e) {
				log.error(this, e);
			}
		}
		this.updateLastIndexTime(objectIds);
	}

	public void removeEntities(List<? extends Entity> entities) throws IOException {
		Set<Long> objectIds = Utils.newHashSet();
		
		Directory directory = FSDirectory.getDirectory(IndexConfig.getIndexDirectory(), false);
	    IndexReader reader = IndexReader.open(directory);
		IndexReader.unlock(directory);   //这里记得要先解锁 
		for(Entity entity: entities) {
			whf.framework.meta.entity.Object meta = MetaManager.getDefaultInstance().findByEntityClass(entity.getClass().getName());
			objectIds.add(meta.getId());
			
			Term term = new Term("uid", getEntityUid(entity));
			int delete = reader.delete(term);
			log.debug("Delete: '" + getEntityUid(entity) + "' " + delete);
		}
		reader.close();
		directory.close();
		//
		this.updateLastIndexTime(objectIds);
	}
	
	public void updateEntities(List<? extends Entity> entities) throws IOException {
		this.removeEntities(entities);
		this.indexEntities(entities);
	}
	
	private void updateLastIndexTime(Set<Long> objectIds) {
		// 更新Object索引时间
		try{
			if(objectIds.size() > 0 ) {
				int count = 0;
				String str = "";
				for(Iterator<Long> ids = objectIds.iterator(); ids.hasNext(); ) {
					if(count == 0) 
						str = String.valueOf(ids.next());
					else 
						str += "," + ids.next();
					count ++;
				}
				ObjectService service = ObjectServiceImp.getObjectService();
				List<whf.framework.meta.entity.Object> objects = service.find("t.id in(" + str + ")");
				for(whf.framework.meta.entity.Object o: objects) {
					Environment.addIndexIgnoredEntity(o);
					Environment.addLogIgnoredEntity(o);
					o.setLastIndexTime(new Timestamp(System.currentTimeMillis()));
					service.update(o);
				}
			}
		} catch (Exception e){
			log.error("Update lastIndexTime failure!", e);
		}
	}
	
	public static String getEntityUid(Entity entity) {
		assert entity != null;
		return entity.getId() + "@" + entity.getClass().getName();
	}
}
